/*
 * Copyright 2008 sempr <iamsempr@gmail.com>
 *
 * Refacted and modified by cqfuture<cqfuture@qq.com> 
 * Bug report email cqfuture@qq.com
 * 
 * This file is part of ZZOJ.
 *
 * ZZOJ is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <mysql/mysql.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/resource.h>

#define BUFFER_SIZE (1024)
#define LOCKFILE    ("/var/run/judged.pid")
#define LOCKMODE    (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
#define STD_MB      1048576

#define OJ_WT0 0 // 等待中
#define OJ_WT1 1 // 等待中
#define OJ_CI 2  // 编译中 
#define OJ_RI 3  // 运行中
#define OJ_AC 4  // 答案正确
#define OJ_PE 5  // 格式错误
#define OJ_WA 6  // 答案错误
#define OJ_TL 7  // 时间超限
#define OJ_ML 8  // 内存超限
#define OJ_OL 9  // 输出超限
#define OJ_RE 10 // 运行错误
#define OJ_CE 11 // 编译错误
#define OJ_CO 12 
static char lock_file[BUFFER_SIZE]=LOCKFILE;
// 程序运行目录
static char oj_home[BUFFER_SIZE] = { 0 };

// mysql数据库配置
static char db_host_name[BUFFER_SIZE] = { 0 };
static char db_user_name[BUFFER_SIZE] = { 0 };
static char db_password[BUFFER_SIZE] = { 0 };
static char db_name[BUFFER_SIZE] = {0};
static int  db_port = 0;

// mysql数据库操作相关参数
#ifdef _mysql_h
static MYSQL *sql_conn = NULL;
static MYSQL_RES *sql_res = NULL;
// 提交记录查询语句
static char solution_query_sql[BUFFER_SIZE];
#endif

// 可评测哪几种语言编写的代码，默认只支持C、C++、Python
// 各种语言使用逗号(,)隔开
// 0 - C语言
// 1 - C++
// 2 - Pascal
// 3 - Java
// 6 - Python
static char oj_lang_support_set[BUFFER_SIZE] = { 0 };
// 运用 solution_id % oj_total的值是否等于oj_mod_ans，实现判题进程的负载均衡
// 例如: oj_total的值为3
// 机器A, oj_mod_ans=0, 则机器A负责测试 solution_id % 3 == 0 的提交记录
// 机器B, oj_mod_ans=1, 则机器B负责测试 solution_id % 3 == 1 的提交记录
// 机器C, oj_mod_ans=2, 则机器C负责测试 solution_id % 3 == 2 的提交记录
static int oj_total = 0;
static int oj_mod_ans = 0;

// 评测配置

#define MAXCNT_RUNNING_PROC    (200) // 系统最大可支持同时运行的进程数

// 评测进程的进程ID列表。
// 若code_judge_proc_ids[i]==0，则表示拥有空闲进程
// 若code_judge_proc_ids[i]!=0，则表示表示已被占用
static pid_t code_judge_proc_ids[MAXCNT_RUNNING_PROC] = { 0 };

// 待评测任务列表
static int job_list[MAXCNT_RUNNING_PROC * 2 + 1] = { 0 };

static int max_running_proc_cnt = 0; // 可同时运行的最大并行进程数
static int running_proc_cnt = 0;     // 正在运行中的评测进程

// 评测进程，每次任务间的休眠时间
static int sleep_time = 0;



// http判题
static int  http_judge = 0;
static char http_baseurl[BUFFER_SIZE];
static char http_username[BUFFER_SIZE];
static char http_password[BUFFER_SIZE];

// redis数据库配置
static int  redis_judge = 0;
static char redis_server[BUFFER_SIZE];
static int  redis_port;
static char redis_auth[BUFFER_SIZE];
static char redis_qname[BUFFER_SIZE];

// 守护进程运行配置
static int  turbo_mode = 0;
static bool STOP = false;
static int  DEBUG = 0; // 是否以调试模式运行
static int  ONCE = 0;

void call_for_exit(int s) 
{
	STOP = true;
	printf("Stopping judged...\n");
}

void write_log(const char *fmt, ...) 
{
	va_list ap;
    char buffer[4096] = {0};
	sprintf(buffer, "%s/log/judge_core.log", oj_home);
	FILE *fp = fopen(buffer, "ae+");
	if (fp == NULL) {
		fprintf(stderr, "openfile error!\n");
		system("pwd");
	}
	va_start(ap, fmt);
	vsprintf(buffer, fmt, ap);
	fprintf(fp, "daemon - %s\n", buffer);
    if (DEBUG) {
        printf("%s\n", buffer);
    }
	va_end(ap);
	fclose(fp);
}

int after_equal(char * c) 
{
	int i = 0;
	for (; c[i] != '\0' && c[i] != '='; i++);
    // 要去除等号，所以要返回i+1
	return i+1;
}

// 去除字符串首尾的空格
void trim(char * c) 
{
    char buf[BUFFER_SIZE] = { 0 };
	char * start, *end;
	strcpy(buf, c);
	// 去除字符串前面的空格
    start = buf;
    while (isspace(*start)) {
        start++;
    }
    // 去除字符串后面的空格
	end = start;
    while (!isspace(*end)) {
        end++;
    }
	*end = '\0';
	strcpy(c, start);
}

// 读取字符串配置项
bool read_str(char * buf, const char * key, char * value)
{
    if ( strncmp(buf, key, strlen(key)) != 0 ) {
        return false;
	}
	strcpy(value, buf + after_equal(buf));
	trim(value);
	if ( DEBUG ) {
	    printf("%s\n", value);
    }
    return true;
}

// 读取整数配置项目
void read_int(char * buf, const char * key, int * value) {
	char buf2[BUFFER_SIZE];
	if (read_str(buf, key, buf2))
		sscanf(buf2, "%d", value);
}

// 初始化系统配置（数据库、服务器地址等）
void init_sys_conf()
{
    char buf[BUFFER_SIZE] = {0};
    // 设置默认配置
    db_host_name[0] = 0;
    db_user_name[0] = 0;
    db_password[0] = 0;
    db_name[0] = 0;
    db_port = 3306;
    max_running_proc_cnt = 3;
    sleep_time = 1;
    oj_total = 1;
    oj_mod_ans = 0;
    // 语言支持,默认只支持C、C++、Python
    // 0 - C语言
    // 1 - C++
    // 2 - Pascal
    // 3 - Java
    // 6 - Python
    strcpy(oj_lang_support_set, "0,1,6");

    // 读取配置文件中的配置
	FILE *fp = fopen("./etc/judge.conf", "r");
    if (fp == NULL) {
        return;
    }

    // 获取配置项
    while (fgets(buf, BUFFER_SIZE - 1, fp))
	{
        read_str(buf, "OJ_HOST_NAME", db_host_name);
        read_str(buf, "OJ_USER_NAME", db_user_name);
		read_str(buf, "OJ_PASSWORD", db_password);
		read_str(buf, "OJ_DB_NAME", db_name);
		read_int(buf, "OJ_PORT_NUMBER", &db_port);
		read_int(buf, "OJ_RUNNING", &max_running_proc_cnt);
        if ( max_running_proc_cnt > MAXCNT_RUNNING_PROC ) {
            max_running_proc_cnt = MAXCNT_RUNNING_PROC;
        }
		read_int(buf, "OJ_SLEEP_TIME", &sleep_time);
        if ( sleep_time <= 0 ) {
            sleep_time = 10;
        }

        // 运用 solution_id % oj_tot的值是否等于oj_mod
        // 来实现判题进程的负载均衡
		read_int(buf, "OJ_TOTAL", &oj_total);
		read_int(buf, "OJ_MOD", &oj_mod_ans);

		read_int(buf, "OJ_HTTP_JUDGE", &http_judge);
		read_str(buf, "OJ_HTTP_BASEURL", http_baseurl);
		read_str(buf, "OJ_HTTP_USERNAME", http_username);
		read_str(buf, "OJ_HTTP_PASSWORD", http_password);
		read_str(buf, "OJ_LANG_SET", oj_lang_support_set);

		read_int(buf, "OJ_REDISENABLE", &redis_judge);
		read_str(buf, "OJ_REDISSERVER", redis_server);
		read_int(buf, "OJ_REDISPORT", &redis_port);
		read_str(buf, "OJ_REDISAUTH", redis_auth);
		read_str(buf, "OJ_REDISQNAME", redis_qname);
		read_int(buf, "OJ_TURBO_MODE", &turbo_mode);
	}
	fclose(fp);

#ifdef _mysql_h
		// 初始化提交代码查询sql语句, result < 2 表示评测中
	sprintf(solution_query_sql,
		"SELECT solution_id FROM solution \
            WHERE language in (%s) and result<2 and MOD(solution_id,%d)=%d \
            ORDER BY result ASC, solution_id ASC limit %d",
		oj_lang_support_set, oj_total, oj_mod_ans, max_running_proc_cnt * 2);
#endif
}

// 运行判题进程
void exec_client_proc(int solutionId, int clientId) 
{
    if (DEBUG) {
        write_log("<<=solution_id=%d===clientid=%d==>>\n", solutionId, clientId);
    }

	// 设置资源限制
	struct rlimit LIM;
	LIM.rlim_max = 800;
	LIM.rlim_cur = 800;
	setrlimit(RLIMIT_CPU, &LIM);

	LIM.rlim_max = 180 * STD_MB;
	LIM.rlim_cur = 180 * STD_MB;
	setrlimit(RLIMIT_FSIZE, &LIM);

	LIM.rlim_max = STD_MB << 11;
	LIM.rlim_cur = STD_MB << 11;
	setrlimit(RLIMIT_AS, &LIM);

	LIM.rlim_cur = LIM.rlim_max = 200;
	setrlimit(RLIMIT_NPROC, &LIM);

	char strClientId[BUFFER_SIZE] = { 0 };
	char strSolutionId[BUFFER_SIZE] = {0};
	sprintf(strSolutionId, "%d", solutionId);
	sprintf(strClientId, "%d", clientId);

	if (!DEBUG) {
		execl("/usr/bin/judge_client", "/usr/bin/judge_client",
            strSolutionId, strClientId, oj_home, (char *)NULL);
	}
	else
	{
		execl("/usr/bin/judge_client", "/usr/bin/judge_client",
            strSolutionId, strClientId, oj_home, "debug", (char *)NULL);
	}
}

#ifdef _mysql_h
int execute_sql(const char * sql) 
{
    if ( sql_conn == NULL ) {
        return -2;
    }
    int ret = 0;
	if ( mysql_real_query(sql_conn, sql, strlen(sql)) != 0 ) {
		if ( DEBUG ) {
			write_log("%s", mysql_error(sql_conn));
		}
		sleep(20);
        mysql_close(sql_conn);
		sql_conn = NULL;
        ret = -1;
	}
	return ret;
}
#endif

#ifdef _mysql_h
// 初始化mysql连接,操作成功返回0,操作失败返回非0
int init_mysql_conn() 
{
	if (sql_conn != NULL) {
		return execute_sql("set names utf8");
	}
	
	// init the database connection
	sql_conn = mysql_init(NULL);
	
	/* connect the database */
	const char timeout = 30;
	mysql_options(sql_conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
    // mysql_real_connect,若连接成功，返回sql_conn的值，若连接不成功返回NULL
	MYSQL *ret = mysql_real_connect(sql_conn,
		db_host_name,
		db_user_name,
		db_password,
		db_name,
		db_port, 0, 0);
	if ( ret == NULL ) {
		if (DEBUG) {
			write_log("%s", mysql_error(sql_conn));
		}
		sleep(2);
		return -1;
	}
	return 0;
}
#endif

FILE * read_cmd_output(const char * fmt, ...) 
{
	char cmd[BUFFER_SIZE];

	FILE * ret = NULL;
	va_list ap;

	va_start(ap, fmt);
	vsprintf(cmd, fmt, ap);
	va_end(ap);
    if (DEBUG) {
        printf("%s\n", cmd);
    }
	ret = popen(cmd, "r");
	return ret;
}

int read_int_http(FILE * f) 
{
    char buf[BUFFER_SIZE] = {0};
	fgets(buf, BUFFER_SIZE - 1, f);
	return atoi(buf);
}

bool check_login() 
{
	const char * cmd =
			"wget --post-data=\"checklogin=1\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s/admin/problem_judge.php\"";
	
	FILE * fjobs = read_cmd_output(cmd, http_baseurl);
    int ret = read_int_http(fjobs);
	pclose(fjobs);
	return ret > 0;
}

void login() 
{
    // 用户已登录
    if (check_login()) {
        return;
    }
    
    // 使用wget登录
    char cmd[BUFFER_SIZE];
    sprintf(cmd,
        "wget --post-data=\"user_id=%s&password=%s\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s/login.php\"",
        http_username, http_password, http_baseurl);
    system(cmd);
}

// 返回待评测的解决方案的数量
int _get_jobs_http(int * jobs) {
	login();

    int cnt = 0;
    char buf[BUFFER_SIZE] = {0};
	const char * cmd =
			"wget --post-data=\"getpending=1&oj_lang_set=%s&max_running=%d\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s/admin/problem_judge.php\"";
	FILE * fjobs = read_cmd_output( cmd, oj_lang_support_set, max_running_proc_cnt, http_baseurl);
	while ( fscanf(fjobs, "%s", buf) != EOF ) {
        int sid = atoi(buf);
        if (sid > 0) {
            jobs[cnt++] = sid;
        }
    }
    pclose(fjobs);
	return cnt;
}

#ifdef _mysql_h
int _get_jobs_mysql(int * jobs) 
{
	// 查询数据库中，最新提交的代码,通过result来判断是否是最新提交的
	if ( mysql_real_query(sql_conn, solution_query_sql, strlen(solution_query_sql)) != 0 ) {
		if (DEBUG) {
			write_log("%s", mysql_error(sql_conn));
		}
		sleep(20);
		return 0;
	}

    // 获取查询结果
	sql_res = mysql_store_result(sql_conn);
    if (sql_res == NULL ) {
        return 0;
    }

    // 读取所有查询到的solution_id
    int cnt = 0;

    MYSQL_ROW row = mysql_fetch_row(sql_res);
	while ( row != NULL ) {
        jobs[cnt++] = atoi(row[0]);
        row = mysql_fetch_row(sql_res);
	}
    
    // 释放结果空间
    if (sql_res != NULL) {
        mysql_free_result(sql_res);
        sql_res = NULL;
    }
    execute_sql("commit");
	return cnt;
}
#endif

int _get_jobs_redis(int * jobs)
{
    int cnt = 0;
    // redis命令：移除并返回列表的最后一个元素
    const char * cmd="redis-cli -h %s -p %d -a %s --raw rpop %s";
    while( cnt <=max_running_proc_cnt ) {
        FILE * fjobs = read_cmd_output(cmd, redis_server, redis_port, redis_auth, redis_qname);
        if( fscanf(fjobs,"%d",&jobs[cnt]) == 1 ) {
            cnt++;
            pclose(fjobs);
        }
        else {
            pclose(fjobs);
            break;
        }
    }
    if ( DEBUG ) {
        printf("redis return %d jobs", cnt);
    }
    return cnt;
}

int get_jobs(int * jobs) {
	if ( http_judge ) {
		return _get_jobs_http(jobs);
	}
	if( redis_judge ) {
		return _get_jobs_redis(jobs);
	}
#ifdef _mysql_h
     return _get_jobs_mysql(jobs);
#endif
}

#ifdef _mysql_h
bool _check_out_mysql(int solution_id, int result) 
{
    char sql[BUFFER_SIZE] = {0};
	sprintf(sql,
			"UPDATE solution SET result=%d,time=0,memory=0,judgetime=NOW() \
            WHERE solution_id=%d and result<2 LIMIT 1",
			result, solution_id);
	if ( mysql_real_query(sql_conn, sql, strlen(sql)) ) {
		syslog(LOG_ERR | LOG_DAEMON, "%s", mysql_error(sql_conn));
		return false;
	}
    
    bool ret = false;
    if (sql_conn != NULL && mysql_affected_rows(sql_conn) > 0ul) {
        ret = true;
    }
    return ret;
}
#endif

bool _check_out_http(int solution_id, int result) 
{
	login();
	const char * cmd =
			"wget --post-data=\"checkout=1&sid=%d&result=%d\" --load-cookies=cookie --save-cookies=cookie --keep-session-cookies -q -O - \"%s/admin/problem_judge.php\"";
	int ret = 0;
	FILE * fjobs = read_cmd_output(cmd, solution_id, result, http_baseurl);
	fscanf(fjobs, "%d", &ret);
	pclose(fjobs);

	return ret;
}

bool check_out(int solution_id, int result) 
{
    if ( redis_judge || oj_total > 1 ) {
        return true;
    }

	if (http_judge) {
		return _check_out_http(solution_id, result);
	} 
    else{
#ifdef _mysql_h
		return _check_out_mysql(solution_id, result);
#endif
	}
    return false;
}

// 等待评测进程运行结束，返回某个评测进程在进程ID列表中对应的索引号
int wait_one_judge_proc_finish()
{
    // 等待该进程的任意子进程运行结束
    // pid<-1   等待进程组识别码为 pid 绝对值的任何子进程。
    // pid = -1 等待任何子进程, 相当于 wait()。
    // pid = 0  等待进程组识别码与目前进程相同的任何子进程。
    // pid>0    等待任何子进程识别码为 pid 的子进程
    // 最后一个参数为options，将其设为0，表示一直等到有子进程结束函数才会返回
    pid_t childPid = waitpid(-1, NULL, 0);
    int idleIdx = -1;
    for (int i = 0; i < max_running_proc_cnt; i++) {
        if (code_judge_proc_ids[i] == childPid)
        {
            code_judge_proc_ids[i] = 0;
            idleIdx = i;
            break;
        }
    }
    return idleIdx;
}

int wait_all_judge_proc_finish()
{
    int finishCnt = 0;
    pid_t chlPid = waitpid(-1, NULL, 0);
    while ( chlPid > 0 ) 
    {
        // 设置clientId标记
        for (int i = 0; i < max_running_proc_cnt; i++) {
            if (code_judge_proc_ids[i] == chlPid ) {
                finishCnt++;
                code_judge_proc_ids[i] = 0;
                break;
            }
        }
        chlPid = waitpid(-1, NULL, 0);
    }
    return finishCnt;
}

int get_one_idle_idx()
{
    int idleIdx = -1;
    for (int i = 0; i < max_running_proc_cnt; i++) {
        if (code_judge_proc_ids[i] == 0) {
            idleIdx = i;
            break;
        }
    }
    return idleIdx;
}

// 返回运行的进程数量
int work() 
{
    int finishCnt = 0;
	// 获取代码提交记录,jobs数组中，保存的内容为最新提交的solution_id
	int jobCnt = get_jobs(job_list);
	if (jobCnt == 0 ) {
		return 0;
	}

	// 评测提交的代码
	for (int i = 0; i < jobCnt; i++) {
        int solutionId = job_list[i];
		// 如下的if，只在拥有多个评测机时使用，用于实现简单的负载均衡，优化用户体验。
		if (solutionId % oj_total != oj_mod_ans) {
			continue;
		}
		if (DEBUG) {
			write_log("Judging solution %d", solutionId);
		}
		
        int idleIdx = -1;
		// 当前运行的评测进程超过最大进程数
		if ( running_proc_cnt >= max_running_proc_cnt ) {
            idleIdx = wait_one_judge_proc_finish();
            if ( idleIdx >= 0 ) {
                running_proc_cnt--;
                finishCnt++;
            }
        }
        else {
            idleIdx = get_one_idle_idx();
        }
        // 若出现未估计错误，则继续执行
        if ( idleIdx < 0 )
        {
            continue;
        }
        
        // 设置solution状态,标记开始评测
        if ( running_proc_cnt < max_running_proc_cnt && check_out(solutionId, OJ_CI) ) {
            running_proc_cnt++;

            pid_t childProcId = fork();

            if (childProcId == 0 ) {
                // 在子进程中返回,则启动判题进程judge_client
                exec_client_proc(solutionId, idleIdx);
                exit(0);
            }
            else if ( childProcId > 0 ) {
                // 在父进程返回，则标记判题进程处于运行中
                code_judge_proc_ids[idleIdx] = childProcId;
            }
            else {
                // fork运行失败
                code_judge_proc_ids[idleIdx] = 0;
            }
		}
	}

    // 等待所有进程运行结束
    int procCnt = wait_all_judge_proc_finish();
    running_proc_cnt -= procCnt;
    finishCnt += procCnt;
    if (DEBUG && finishCnt > 0) {
        write_log("<<%d judge proc done!>>", finishCnt);
    }
	return finishCnt;
}

int lockfile(int fd) {
	struct flock fl;
	fl.l_type = F_WRLCK;
	fl.l_start = 0;
	fl.l_whence = SEEK_SET;
	fl.l_len = 0;
	return (fcntl(fd, F_SETLK, &fl));
}

int already_running()
{
    char buf[16] = {0};
	int fd = open(lock_file, O_RDWR | O_CREAT, LOCKMODE);
	if (fd < 0) {
		syslog(LOG_ERR | LOG_DAEMON, "judged can't open %s: %s", lock_file, strerror(errno));
		exit(1);
	}
	if (lockfile(fd) < 0) {
	    // 程序已启动
	    if ( errno == EACCES || errno == EAGAIN ) {
			close(fd);
			return 1;
		}
		syslog(LOG_ERR | LOG_DAEMON, "can't lock %s: %s", lock_file, strerror(errno));
		exit(1);
	}
    // 将文件大小设置为0
	ftruncate(fd, 0);
	sprintf(buf, "%d", getpid());
	write(fd, buf, strlen(buf) + 1);
	return 0;
}

int daemon_init(void)
{
	// 创建进程
	// 1. 在父进程中，fork返回新创建的子进程的ID
	// 2. 在子进程中，fork返回0
	// 3. 如果出现错误，则返回负值。出错原因如下：
	//    (1) 当前的进程数已经达到了系统规定的上限，这时errno的值被设置为EAGAIN。
	//    (2) 系统内存不足，这时errno的值被设置为ENOMEM。
    pid_t pid = fork();
    if ( pid < 0) {
        return  -1;
    }
	else if ( pid != 0 ) {
	    exit(0); // 结束父进程
	}
	
	// 设置会话ID(session id),让子进程脱离与父进程的关联
	// 父进程退出后，子进程不会退出。实现子进程后台运行
	setsid();
	// 切换进程的工作目录
	chdir(oj_home);

    /*
    umask 主要作用:设置允许当前进程创建文件或者目录最大可操作的权限
          文件的访问允许权限共有9种，分别是：rwx-rwx-rwx
          分别代表：用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行。

    umask(0)，表示0取反在创建文件时权限进行与运算，即：(~0) & mode 等于八进制的值0777 & mode，
              表示给后面的代码调用函数mkdir给出最大的权限，避免创建目录或文件的权限不确定性。
    */
	umask(0);

	// 关闭输入输出终端
	close(0); /* close stdin */
	close(1); /* close stdout */
	close(2); /* close stderr */

	int fd = open( "/dev/null", O_RDWR );
	dup2( fd, 0 );
	dup2( fd, 1 );
	dup2( fd, 2 );
	if ( fd > 2 ){
		close( fd );
	}
	return 0;
}

void turbo_mode2()
{
#ifdef _mysql_h
    if ( sql_conn == NULL ) {
        return;
    }
	if(turbo_mode==2){
        char sql[BUFFER_SIZE] = {0};
		sprintf(sql," CALL `sync_result`();");
		mysql_real_query( sql_conn, sql, strlen(sql) );
	}
#endif
}

int main(int argc, char* argv[])
{
    DEBUG = (argc > 2);
    ONCE = (argc > 3);
	// 设置工作目录，默认目录为/home/judge
    if ( argc > 1 ) {
        strcpy(oj_home, argv[1]);
    }
    else {
        strcpy(oj_home, "/home/judge");
    }

	// 切换工作目录到/home/judge
	chdir(oj_home);

	// 程序运行lock，用于判断后台进程是否正在运行中
	sprintf(lock_file,"%s/etc/judge.pid",oj_home);

	// 非调试模式下，设置进程后台运行
	if ( !DEBUG ) {
	    daemon_init();
	}

	// 程序已启动
	if ( already_running()) {
		syslog(LOG_ERR | LOG_DAEMON, "This judged daemon program is already running!\n");
		printf("%s already has one judged on it!\n", oj_home);
		return 1;
	}

	// 添加防火墙规则
	if( !DEBUG ) {
	    system("/sbin/iptables -A OUTPUT -m owner --uid-owner judge -j DROP");
	}

	// 初始化系统配置
	init_sys_conf();

    /*
    根据进程信号,设置程序运行结束标记
    SIGQUIT - ctrl + \
    SIGKILL - 运行kill命令
    SIGTERM - 系统关闭，程序运行异常，运行程序的终端关闭
	*/
    signal(SIGQUIT, call_for_exit);
	signal(SIGKILL, call_for_exit);
	signal(SIGTERM, call_for_exit);

	int judgedCnt = 1;
	int totalJudgedCnt = 0;
	// 程序主循环
	while (1)
	{
        do
        {
#ifdef _mysql_h
            // 初始化mysql连接,多次调用时，不会重复连接
			init_mysql_conn();
#endif
			// 获取solution，执行评测操作
            judgedCnt = work();
            totalJudgedCnt += judgedCnt;
            if (turbo_mode == 2 ) {
                if ( totalJudgedCnt > max_running_proc_cnt * 10 || judgedCnt < max_running_proc_cnt ) {
                    turbo_mode2();
                    totalJudgedCnt = 0;
                }
            }

#ifdef _mysql_h
            // 释放结果空间,防止内存泄漏
            if (sql_res != NULL ) {
                mysql_free_result(sql_res);
                sql_res = NULL;
            }
            execute_sql("commit");
#endif
			if ( ONCE ) {
				break;
			}

		} while (judgedCnt > 0 );

		turbo_mode2();
		if ( ONCE ) {
			break;
		}
		sleep(sleep_time);
	}
	return 0;
}
